﻿ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Reflection;
using Metaspec;

// Reparsing example.

namespace ReparsingExample
{
	class Program
	{
		static void Main(string[] args)
		{
			//creating project
			ICsProject project = ICsProjectFactory.create(project_namespace.pn_project_namespace);
			project.setBuildEntityModel(true);

			string directory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), @"..\..");
			ICsFile file1;
			ICsFile file2;
			{
				//adding assemblies
				string assembly_path = Assembly.GetAssembly(typeof(object)).Location;
				byte[] assembly_buffer = File.ReadAllBytes(assembly_path);
				IExternalAssemblyModule module = IExternalAssemblyModuleFactory.create(assembly_buffer, assembly_path);
				project.addExternalAssemblyModules(new IExternalAssemblyModule[] { module }, false, null);

				//adding C# files
				{
					string path = Path.Combine(directory, "Test1.cs");
					char[] buffer = File.ReadAllText(path).ToCharArray();
					file1 = ICsFileFactory.create(buffer, path);
					project.addFiles(file1);
				}
				{
					string path = Path.Combine(directory, "Test2.cs");
					char[] buffer = File.ReadAllText(path).ToCharArray();
					file2 = ICsFileFactory.create(buffer, path);
					project.addFiles(file2);
				}
			}

			//creating solution
			ISolution solution = ISolutionFactory.create();
			solution.setLogMessageCallback(Console.WriteLine);
			solution.addProjects(project);

			//parsing
			solution.parse();

			//checking: Test1.C1.f() invoke f(int)
			{
				CsCompilationUnit unit = file1.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[2];
				CsBlock b = m.definition;
				CsExpressionStatement s = (CsExpressionStatement)b.statements[0];
				CsInvocationExpression e = (CsInvocationExpression)s.expression;
				CsEntityMethod entity = (CsEntityMethod)e.entity;
				CsMethod im = (CsMethod)entity.decl;
				Debug.Assert(im.line_info.Line == 5); // invoke f(int)
			}
			//checking: Local variable entity i in Test2.C2.f() method exists
			{
				CsCompilationUnit unit = file2.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock b = m.definition;
				CsLocalVariableDeclaration d = (CsLocalVariableDeclaration)((CsDeclarationStatement)b.statements[0]).declaration;
				CsLocalVariableDeclarator d2 = d.declarators.First.Value;
				Debug.Assert(d2.entity != null);
			}

			//removing
			project.iRemoveNodesAndEntities(file1);

			//checking: file1 AST tree is removed
			{
				CsCompilationUnit unit = file1.getCompilationUnit();
				Debug.Assert(unit == null);
			}
			//checking: Local variable entity i in Test2.C2.f() is removed
			{
				CsCompilationUnit unit = file2.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock b = m.definition;
				CsLocalVariableDeclaration d = (CsLocalVariableDeclaration)((CsDeclarationStatement)b.statements[0]).declaration;
				CsLocalVariableDeclarator d2 = d.declarators.First.Value;
				Debug.Assert(d2.entity == null);
			}

			//changing C# file
			{
				string path = Path.Combine(directory, "Test1_2.cs");
				char[] buffer = File.ReadAllText(path).ToCharArray();
				file1.setBuffer(buffer);
			}

			//reparsing file1
			project.iBuildNodesAndEntities(file1);

			//checking: Test1.C1.f() invoked entity is removed
			{
				CsCompilationUnit unit = file1.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[2];
				CsBlock b = m.definition;
				CsExpressionStatement s = (CsExpressionStatement)b.statements[0];
				CsInvocationExpression e = (CsInvocationExpression)s.expression;
				CsEntityMethod entity = (CsEntityMethod)e.entity;
				CsMethod im = (CsMethod)entity.decl;
				Debug.Assert(im.line_info.Line == 9); // invoke f(long)
			}
			//checking: Local variable entity i in Test2.C2.f() is still removed
			{
				CsCompilationUnit unit = file2.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock b = m.definition;
				CsLocalVariableDeclaration d = (CsLocalVariableDeclaration)((CsDeclarationStatement)b.statements[0]).declaration;
				CsLocalVariableDeclarator d2 = d.declarators.First.Value;
				Debug.Assert(d2.entity == null);
			}

			//parse all files
			solution.parse();

			//checking: Local variable entity i in Test2.C2.f() method exists
			{
				CsCompilationUnit unit = file2.getCompilationUnit();
				CsNamespace n = (CsNamespace)unit.declarations[0];
				CsClass c = (CsClass)n.member_declarations[0];
				CsMethod m = (CsMethod)c.member_declarations[0];
				CsBlock b = m.definition;
				CsLocalVariableDeclaration d = (CsLocalVariableDeclaration)((CsDeclarationStatement)b.statements[0]).declaration;
				CsLocalVariableDeclarator d2 = d.declarators.First.Value;
				Debug.Assert(d2.entity != null);
			}
		}

		static CsEntity getEntity(CsScopeEntity scope, params string[] names)
		{
			CsEntity entity = null;
			for (int i = 0; i != names.Length; ++i)
			{
				entity = scope.scope_table.get_single_id(names[i]);
				if (i != names.Length - 1)
				{
					scope = (CsScopeEntity)entity;
				}
			}
			return entity;
		}
	}
}
